package com.lhczf.lucenedb.example;

import com.jsoniter.output.JsonStream;
import com.lhczf.lucenedb.bean.RecordBean;
import com.lhczf.lucenedb.service.BusinessServer;
import com.lhczf.lucenedb.util.BeanFactory;
import com.lhczf.lucenedb.util.LuceneUtil;
import com.lhczf.lucenedb.util.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.facet.FacetResult;
import org.apache.lucene.facet.Facets;
import org.apache.lucene.facet.FacetsCollector;
import org.apache.lucene.facet.FacetsConfig;
import org.apache.lucene.facet.taxonomy.FastTaxonomyFacetCounts;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.index.*;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.grouping.GroupDocs;
import org.apache.lucene.search.grouping.GroupingSearch;
import org.apache.lucene.search.grouping.TopGroups;
import org.apache.lucene.util.BytesRef;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@Slf4j
public class RestFulManager {

    @Resource
    private LuceneUtil luceneUtil;

    @GetMapping(path = "/all")
    public ResponseEntity<String> index() {
        Map<String, Object> baseInfo = new HashMap<>(16);
        String dataDir = SpringUtil.getProperValue("system.lucenedb.data.root");
        baseInfo.put("当前系统监控目录", new File(dataDir).getAbsolutePath());

        dataDir = SpringUtil.getProperValue("system.lucenedb.index.root");
        baseInfo.put("索引保持目录", new File(dataDir).getAbsolutePath());

        dataDir = SpringUtil.getProperValue("system.lucenedb.index.threads");
        int config = Integer.parseInt(dataDir);
        int availa = Runtime.getRuntime().availableProcessors();
        baseInfo.put("并发创建索引线程数", config > availa ? availa : config);

        Map<String, String> restfuls = new HashMap<>(16);
        restfuls.put("/queryDataToal", "查询当前索引中的总数据量");
        restfuls.putIfAbsent("/facetByFieldName/{fieldName}", "按指定字段来分组所有数据,查询时将{fileName}替换成具体的字段名称");
        baseInfo.put("当前系统支持restful接口", restfuls);

        return new ResponseEntity<>(JsonStream.serialize(baseInfo), HttpStatus.CREATED);
    }

    @GetMapping(path = "queryDataToal")
    public ResponseEntity<String> queryDataTotal() {
        BusinessServer server = SpringUtil.getBean(BusinessServer.class);
        long start = System.currentTimeMillis();
        int total = server.getTotal();
        long end = System.currentTimeMillis();
        long totalTime = end - start;

        StringBuilder stringBuffer = new StringBuilder("整个数据库中的记录数为：");
        stringBuffer.append(total).append("，统计耗时：").append(totalTime).append("毫秒。");

        return new ResponseEntity<>(stringBuffer.toString(), HttpStatus.CREATED);
    }

    @GetMapping(path = "distinctByFieldName/{fieldName}")
    public ResponseEntity<String> distinctByFieldName(@NotNull @PathVariable String fieldName) {
        List<String> distinctData = new ArrayList<>();
        IndexReader[] readers = luceneUtil.getIndexReaders();
        try (MultiReader multiReader = new MultiReader(readers, true)) {
            if (multiReader.leaves().isEmpty()) {
                return new ResponseEntity<>("无数据", HttpStatus.CREATED);
            }
            List<LeafReaderContext> leaves = multiReader.leaves();
            for (LeafReaderContext leafReaderContext : leaves) {
                SortedDocValues docVals = DocValues.getSorted(leafReaderContext.reader(), fieldName);
                int ord;
                while ((ord = docVals.nextDoc()) != SortedSetDocValues.NO_MORE_ORDS) {
                    if (ord >= docVals.getValueCount()) {
                        break;
                    }
                    String name = docVals.lookupOrd(ord).utf8ToString();
                    distinctData.add(name);
                }
            }
        } catch (IOException e) {
            log.error("", e);
        }
        return new ResponseEntity<>(JsonStream.serialize(distinctData), HttpStatus.CREATED);
    }

    @GetMapping(path = "groupByRuleName/{fieldName}")
    public ResponseEntity<String> groupByFiledName(@NotNull @PathVariable String fieldName) {
        GroupingSearch groupingSearch = new GroupingSearch(fieldName);
        //如果不设置下面这个属性，每个分组里面只有一条数据，下面的配置每个组最多返回2条
        groupingSearch.setGroupDocsLimit(2);

        Map<String, List<RecordBean>> groupMap = new HashMap<>();

        IndexSearcher searcher = luceneUtil.makeCommSearcher();
        Query query = IntPoint.newRangeQuery("riskLev", 0, 5);

        TopGroups<BytesRef> result = null;
        String message = "查询过程中出现异常";
        try {
            result = groupingSearch.search(searcher, query, 0, 1000);
        } catch (IOException e) {
            log.error("查询过程中出现异常，查询内容为：{}", query.toString());
        }
        if (result == null) {
            log.error(message);
            return new ResponseEntity<>(message, HttpStatus.CREATED);
        }
        for (GroupDocs<BytesRef> groupDocs : result.groups) {
            String groupValue = groupDocs.groupValue.utf8ToString();
            groupMap.computeIfAbsent(groupValue, k -> new ArrayList<>());

            for (ScoreDoc scoreDoc : groupDocs.scoreDocs) {
                RecordBean bean = null;
                try {
                    bean = (RecordBean) BeanFactory.getBean(searcher.doc(scoreDoc.doc), RecordBean.class);
                } catch (IOException e) {
                    log.error(message, e);
                }
                groupMap.get(groupValue).add(bean);
            }
        }
        return new ResponseEntity<>(JsonStream.serialize(groupMap), HttpStatus.CREATED);
    }

    @GetMapping(path = "facetByFieldName/{fieldName}")
    public ResponseEntity<String> facetByFieldName(@NotNull @PathVariable String fieldName) throws IOException {
        Map<String, Object> groupMap = new HashMap<>();
        FacetsCollector fc = new FacetsCollector();

        IndexSearcher searcher = luceneUtil.makeCommSearcher();
        String indexDir = SpringUtil.getProperValue("system.lucenedb.index.root");
        DirectoryTaxonomyReader taxoReader = luceneUtil.getTaxonmyReader(indexDir);

        searcher.search(new MatchAllDocsQuery(), fc);
        Facets facets = new FastTaxonomyFacetCounts(taxoReader, new FacetsConfig(), fc);

        String[] names = fieldName.split(",");
        for (String name : names) {
            Map<String, Object> subMap = new HashMap<>(16);
            FacetResult facetResult = facets.getTopChildren(10, name);
            if (facetResult != null) {
                for (int i = 0; i < facetResult.childCount; i++) {
                    subMap.putIfAbsent(facetResult.labelValues[i].label, facetResult.labelValues[i].value);
                }
            }
            groupMap.putIfAbsent(name, subMap);
        }

        taxoReader.close();
        return new ResponseEntity<>(JsonStream.serialize(groupMap), HttpStatus.CREATED);
    }
}
